技术圣战实为协调难题

原文:Technology Holy Wars are Coordination Problems · Gwern.net

Python设计经济学深度洞察技术社会学

围绕平台选择和版本升级的争论之所以如此激烈,并非因为人们喜欢挑刺,而是因为这些选择将影响整个技术生态系统。某个平台可能通过网络效应获益并避免「比特腐烂」,同时却会通过「比特蔓延」微妙地破坏其他平台。

2020-06-15–2020-07-09 ; 已完成 ; 可信度: 较高 ; 重要性: 3反向链接

计算机领域中长期存在的圣战现象,比如 Python 2 向 Python 3 迁移过程中的激烈争论,并非源于人们的心胸狭窄或对冲突的热衷,而是因为这本质上是一个协调问题:关键不在于让每个人都做出明智的决定,而在于让所有人做出一样的决定。一个看似不流行的平台所面临的表面问题,实际上可能是人们试图协调使用不同平台的未明言努力的一部分:主导平台能够享受强大的网络效应,例如由于众多用户的持续使用和维护而减少比特腐烂。与此同时,它还会对其他平台施加一种镜像效应,即比特蔓延,导致这些平台逐渐被忽视并开始衰败。

比特蔓延的直接负面影响不仅使早期采用者失去潜在的网络效应,更严重的是,顽固分子的存在大大降低了新技术或新事物的价值,甚至可能导致早期采用者的处境每况愈下,日常生活变得更加艰难。考虑到顽固分子从社区中获得的巨大利益,采用者往往将顽固分子的行为视为一种寄生且不道德的行为。而顽固分子则否认自己有任何道德义务,并对采用者为增加采用率而采取的手段(如在缺乏正式管控的情况下采用非正式手段,比如霸凌)感到愤慨。

这种迫切需要决出一个胜者的心态,以及选择胜利或失败一方所带来的巨大技术上的好处或代价,解释了圣战中(只是表面上的)不成比例的能量、仇恨和难以妥协的态度。

也许,如果我们能够明确地将圣战理解为一个协调问题,我们就能避免最糟糕的过度行为,并利用这方面的知识来更好地管理诸如编程语言迁移等事务。

沉默的狗:圣战不存在的地方;选择至关重要;网络效应

无论你为极客们打造何种交流平台,不管是 UsenetFidoNet、论坛、Twitter、GithubHacker News、Reddit 还是其他任何平台,早晚都会爆发圣战——那些永无休止的争论。这些争论往往围绕着技术 A 或 B 的优劣展开,争论者甚至会认为使用另一种技术的人不仅头脑简单,而且反社会,简直是在亵渎所有美好和纯粹的事物。在计算机领域,这种周期性出现的「圣战」现象在 Jargon File 中被定义为

网络论战……Danny Cohen 的一篇题为「论圣战与和平呼吁」的论文,使「大端」和「小端」这两个术语在关于字节序(最低位优先/最高位优先)的争议中广为人知,其标题本身就颇具讽刺意味。

历史上著名的「圣战」包括:ITS 对阵 Unix、Unix 对阵 VMSBSD Unix 对阵 System VC 语言对阵 Pascal、C 语言对阵 FORTRAN 等。到了 2003 年,当时流行的争议则是 KDE 对阵 GNOMEvim 对阵 elvisLinux 对阵 [Free|Net|Open]BSD。而经久不衰的争论则包括 EMACS 对阵 vi。[1]

……区别「圣战」与普通技术争议的特征在于:在「圣战」中,大多数参与者都试图将个人价值观和文化偏好伪装成客观的技术评估。之所以会出现这种情况,恰恰是因为在真正的「圣战」中,双方的实质性差异往往相对较小。

**沉默的狗:圣战不存在的地方。**我们或许还能举出更多例子。比如,在互联网普及之前,曾爆发过协议战争;随后,20 世纪 90 年代轰轰烈烈的游戏机大战中,人们对 SNES 和世嘉创世纪的优劣进行了无休止且常常激烈的辩论(接着是 N64 对阵 PS1,PS2 对阵 Xbox...)。再往后 20 年,社交媒体领域有 Usenet、Slashdot、digg、Reddit、Hacker News、Twitter 和 Mastodon 之争,加密货币界则有比特币以太坊(以及门罗币Zcash 等)的角逐。我们还应该注意到,有些事物并不会引发圣战:比如在 grep 这样的工具中选用哪种字符串搜索算法,个人偏好 FPS 还是 RPG 类型的电子游戏,或是该购买多大容量的硬盘、多少核心的 CPU[2] 等。这些话题可能偶尔会引发讨论,但往往会以理性且富有成效的方式进行,这与圣战截然不同。这种现象也不仅仅是「品牌忠诚度」的体现——有无数拥有忠实追随者的品牌并未引发圣战,即便有人讨论,大多数人也只会耸耸肩,得出「品味无可争辩」的结论。

选择至关重要。技术圈的「圣战」真的只是极客们的一场无聊游戏吗?表面上看,争论确实常常是一种消遣。然而,这显然不是全部原因。这些争论往往演变成令人不快的口水战,不仅无人能从中获得乐趣,反而会让所有参与者心怀怨恨。与 ESR 的观点相反,这些技术差异通常相当显著。(即便是他自己举的例子,如 ITS 与 Unix、C 与 Pascal 或 Emacs 与 vi 之间的对比,都体现了截然不同的技术理念。)Armin Ronacher 指出,为了推动技术升级(如从 Python 2 到 3 的转换)而造成的「情感困扰」,似乎与其带来的直接收益不成正比,难以与随之而来的种种纷争相称。他对此感到困惑,只能将「圣战」解释为人们享受「共同受苦」并站在「道德制高点」上的心理,但这种解释显然不够充分(毕竟,为什么人们要主动选择受苦呢?)。尽管 Ronacher 没有深究这个问题,但他无意中透露了一个颇具启发性的观察:「实际上很少有项目足够庞大,能够让第三方分叉后依然存活下来。」⁠[3]

网络效应。有什么不同?某些技术因用户规模扩大而愈发珍贵,另一些则可能因此贬值——基础设施是指那些随使用人数增加而变得更加实用的事物,而应用程序则往往适得其反。所谓的「圣战」(犹如政治辩论)总是围绕着具有网络效应的基础设施或平台展开,其中潜在的连锁反应通常比表面影响更为深远。尽管人们可以选择「分叉」,但「退出」这一选项往往只是表面文章:你选用的操作系统、编程语言或二进制编码方式不仅影响自身,也牵动他人,这是一种相互作用。因此,「发声」成为唯一的选择。人们会为自己钟爱的大型多人在线游戏(MMO)争论不休,却不会为单机纸牌游戏如此热衷,这是因为如果一款 MMO(或任何多人游戏)吸引不到足够的玩家,就无人可玩。即便是两个邻家孩子为游戏主机争论,如果他们能就哪款主机最佳达成一致,也能从中受益,因为这样他们就可以轻松共享游戏、存档和游戏杂志。你的选择对他人而言举足轻重。

相较而言,你如何使用这些技术对他人的影响则相对较小。举例来说,一旦你选定了 Microsoft Excel 而非 VisiCalc 或 Lotus-1-2-3 作为电子表格工具,你在这些表格中填写的具体内容对他人的重要性就远不如你成为 Excel 用户这一事实本身。

(互联)网络效应

数组索引到底应该从 0 开始还是从 1 开始?我提出的「从 0.5 开始」这个折中方案却被毫不留情地否决了,我觉得他们连认真考虑都没有。

Stan Kelly-Bootle(著名计算机幽默作家)[4]

不择手段也要扩大网络。 假设我使用 Python 3,如果我能说服其他人放弃 Python 2 而使用 Python 3,那么无论 Python 3 的改进对他们来说是否真的重要,或者我的理由多么站不住脚,我的处境都会好得多。[5] 选择最流行编程语言的程序员仿佛走在一条被上帝眷顾的康庄大道上:各种库唾手可得,那些使用小众语言的程序员日常碰到的 bug 在这里神奇地销声匿迹,文档总能完美解决他的需求,还附带随取随用的代码片段,API 返回的数据格式恰到好处地契合他的默认数据结构,功能强大的 IDE 会自动补全代码,Stack Overflow 上堆满了解决各种小问题的妙招(这些小问题几乎成了工作之余的趣味游戏),因为在这个世界里,一切都是最好的安排……[6] 诚然,在显性和隐性知识上需要投入巨大的个人和集体资源(甚至要学会为特定工具重塑思维模式),但一旦跨过这道门槛,后面的日子就会过得舒心如意。与之形成鲜明对比的是,那些冒险选择非主流路线的人很快就会领教到:他们不得不自己编写大量库,许多标准的规范粗糙不堪,操作系统或 API 设下了诸多隐形陷阱(随时准备给你致命一击),几乎找不到可靠的参考资料或值得请教的同行,琐碎的问题接踵而至,让人应接不暇(他们越是勤勉地提交 bug 或补丁,就越陷入这个无底洞)。在最黑暗的时刻,他们的信念开始动摇,不禁自问:莫非自己钟爱的语言并非世界上最卓越的编程语言?

不仅是零和博弈,更是负和博弈。平台的正面网络效应是显而易见的。然而,有人可能认识到这些效应的存在,却认为它们还不足以激发如同圣战般的狂热竞争。因此,我们有必要探讨一个更为微妙的问题:一个平台的成功如何通过争夺共享资源而间接损害其他平台

比特腐烂

永远处于变化之中;没有银弹;常用者兴,弃用者衰

永远处于变化之中。比特腐烂」这个术语生动地概括了此类平台所面临的网络效应问题。程序的「腐烂」并非源于它们在存储介质上的变化,也不是单纯因为时间流逝而产生了缺陷(当然也有例外,如千年虫问题)。实际上,比特腐烂发生在程序周围的环境发生变化时。这种变化可能表现为:API 修改了某个字段名;或者字段名保持不变,但其含义已经改变;又或者某个不起眼的功能因为其他地方的变动而失效,直到你的程序像往常一样调用它时才发现问题;还可能是版本号更新导致其他部分无法正常工作,因为它们依赖于旧版本且不确定新版本是否安全;或者你需要安装一个特定程序,但这需要升级现有程序(从而可能影响成千上万的其他程序);又或者系统备份使用的密钥悄然过期、存储空间耗尽,或者未能及时更新以备份新增的服务器。依赖关系和系统交互的隐蔽性意味着我们往往不自觉地走上了「最小阻力路径」,因为我们甚至没有意识到存在这样一条路径,或者正在被某种方式所束缚。(「你说『字节不总是 8 位』是什么意思‽」)我们必须积极对抗比特腐烂:如果你真的想避免建立过多的依赖关系,就需要采用一些策略,比如混沌工程,或者有意使那些超出预期可靠性的服务崩溃。

没有银弹。 诸如类型系统等技术解决方案可以通过精确定位危险变更来帮助缓解比特腐烂,但总有超出类型系统范畴的情况,而唯一真正的端到端测试就是终端用户。[7]

常用者兴,弃用者衰。 避免比特腐烂没有捷径可走:只有经常使用的才能得到良好维护。广泛应用的程序之所以能够避免比特腐烂,与其说是因为它们拥有本质上优越的设计,不如说是得益于围绕它们形成的生态系统:破坏性变更会立即被最先体验的用户发现(从而使其他用户免受影响),或者这些变更根本就不会发生(因为开发者事先进行了测试和检测,或充分意识到了后果,又或者仅仅是因为这些变更首先破坏了他们自己的系统!)。此外,每个人都会调整自己的程序以适应流行的程序,而非反其道而行之。通过这种方式,程序及其生态系统不断更新焕新,避免了比特腐烂——就像活体组织通过不断更新来避免腐烂(细菌等引起的分解)一样。

比特蔓延

我们都有足够的力量去忍受他人的不幸。

François de La Rochefoucauld,格言第 19 条

衰亡的系统「比特腐烂」,成长的系统「比特蔓延」; 「拥抱,扩展,消灭。」; 甲之比特蔓延,乙之标准化。

衰亡的系统「比特腐烂」,成长的系统「比特蔓延」。 「比特腐烂」的反面是我们可称之为比特蔓延的现象:由于时间和精力有限,一个成功避免比特腐烂的系统必然会经历「比特蔓延」。在这个过程中,其他程序开始「比特腐烂」,因为它们越来越依赖于该系统,默认其存在,并且仅在该系统环境下进行测试。这种扭曲逐渐蔓延到整个技术生态系统。在一个严重比特蔓延的环境中,尝试用非主流方式完成任务就像在 La Brea 沥青坑中挣扎,而非在「图灵沼泽」中摸索,这种环境不可避免地会将新程序推向主导系统。它演变成一个技术黑洞,扭曲周围的「空间」;一旦程序积累了足够多的隐式和显式依赖,它就越过了「事件视界」,再也无法脱离(因为从头重写程序反而更简单)。由于替代方案被采用,它们也就得不到维护。这意味着,随着比特蔓延的扩散,那些试图以曾经有效的方式与旧程序交互的人会发现,尽管他们的程序本身没有改变,却遭受了比特腐烂。所有既往的投资都面临风险,在不知不觉中逐渐贬值。[8⁠](这揭示了系统的两种进化策略:要么简单到无人想要替换,要么复杂到无人能够替换。)

「拥抱,扩展,消灭。」——这是一种著名的技术策略。GCC 的整体架构和 Linux 内核有意破坏内部二进制兼容性是两个典型案例,而 systemd 似乎正在成为第三个。

Richard Stallman(RMS)解释了为何不采用模块化组件反复处理输入这种看似合理的方式来设计 GCC。他认为,这种精心设计的架构会使 GCC 变得过于容易修改——敌对方可能会提供闭源的前端或后端,从而损害自由开源软件(FLOSS)的传播。相反,如果 GCC 的结构使得闭源修改几乎无法维护,那么这些修改就不得不开源。无论这种观点是否有道理,不可否认的是,GCC 确实没有遭受太多「安卓化」的影响。与之相对的是,非 GPL 许可的高度模块化 LLVM 编译器不得不从零开始。(然而,LLVM 在工业界和学术界的广泛采用也凸显了这种「要么按我的方式,要么走人」策略的代价。)

Linux 内核开发者为其不支持私有模块的立场辩护。他们认为,通过仅支持那些被纳入 Linux 源代码库的模块(这些模块必须采用自由/开源软件许可证),他们能够自由地修复 bug、解决安全漏洞,甚至重写整个内核。从长远来看,这种做法带来的好处远胜于为了避免破坏多年前发布且此后一直未经修改的不透明二进制模块而冻结一切的做法[9]。然而,这种观点在 GPU 和 Android 等行业中并不受欢迎。为了应对这一挑战,谷歌付出了巨大努力,不仅为 Android 维护了一个永久的 Linux 内核分支,还为下游供应商创建了一个专门的稳定应用二进制接口(ABI)。此外,谷歌还约每隔几年就会重写一次相当于其整个代码库的内容。

第三个例子(仍在进行中)可能是 systemd 在 Linux 中的蔓延(历史):这个最初只是引导脚本的替代品,如今已渗透到从桌面环境到音频守护进程的方方面面;越来越多的操作只能通过 systemd 来完成。那些曾经可以独立运作并与其他工具兼容的组件,现在都默认用户只使用 systemd,并将其功能专门定制为适配 systemd。systemd 已经根深蒂固到难以清除的地步:移除它会导致太多功能失效,甚至那些表面上与 Linux 启动无关的功能也不能幸免。它甚至已经「感染」Solaris 和 FreeBSD。设想一下,某人突然发现自己的关键脚本被 systemd 破坏,这并非他的过错,然而他又无法在不损坏系统的情况下移除 systemd。最后一个不含 systemd 的操作系统版本已是多年前的产物,使用它不仅会导致许多其他程序无法运行,还会带来严重的安全隐患。如果这个人因为种种原因而厌恶 systemd,我们也就不难理解他为何会选择在电子游戏中寻求慰藉了。而对于 systemd 的狂热支持者来说,仅仅让 systemd 成功还远远不够——其他人必须失败。

**甲之比特蔓延,乙之标准化。**比特腐烂和比特蔓延本身并非非黑即白,它们既不完全有害,也不全然有益。[10] 世界在不断变化,这种变化往往出于正当理由,而比特腐烂正是这种变化不可避免的副产品;然而,当变化源于不当原因时,随之而来的比特腐烂就会产生负面影响。要对抗比特腐烂,就需要持续投入资源维护程序,但有时候顺其自然,任其发生可能更为明智;过分追求向后兼容和维护遗留系统可能会造成重大失误(比如 Makefiles 在明显是个错误的情况下仍保持对制表符的敏感,仅仅因为创建者过于担心影响他区区几十个用户,又或者微软 Windows 为了兼容运行所有 DOS 程序而背负的沉重包袱,不管这些程序有多么问题百出)。

比特蔓延在强行推广劣质技术时确实会带来负面影响,但它同时也是技术融合和标准化的必经之路。要抵制比特蔓延,整个技术社区需要不断投入资源探索替代方案并加以实施,但有时候顺其自然,将精力集中在更重要的事情上可能更为明智。举例来说,如果将字节定义为 8 位这一概念通过比特蔓延逐渐在整个生态系统中普及,这未必是件坏事——毕竟,让软件生态系统完全不依赖于字节大小,去兼容那些使用 7 位(更不用说 48 位)字节的老旧系统,究竟能带来多大的实际价值呢?

平台的兴衰

一切残酷行为皆源于软弱。

Seneca the Younger《论幸福生活》第 3 卷

全面战争:最好永不发动,但一旦开始——就必须取胜!;糟糕的妥协只会让所有人都不满意; 战争,以其他形式进行

全面战争:最好永不发动,但一旦开始——就必须取胜! 然而,比特腐烂/比特蔓延却产生了意想不到的后果,它极大地提高了个人选择的风险,将其转变为一场胆小鬼博弈

一个平台越是全面,将他人拉入麾下就越发重要。这不仅是为了享受正面外部性带来的红利,更是为了防止比特腐烂和竞争对手的比特蔓延。[11] 由此衍生出技术圣战的等级制:字节序 > 操作系统 > 编程语言 > 编辑器 > 桌面环境……最终沦为貌似严肃实则玩笑的争论,诸如应该用制表符还是空格。(这也解释了为何某些圣战会偃旗息鼓。人们不再争论 Emacs 和 vim 孰优孰劣,并非因为达成了共识或其中之一退出了舞台,而是因为两者如今都已沦为小众编辑器,它们更多地面临着来自 Visual Studio CodeEclipseSublime Text 等巨头的共同威胁。[12])真正举足轻重的不是胜利者的优点几何,而是必须一个胜利者:「更糟就是更好」——只要 JavaScript 程序员数量够多,就能把一根面条或 NodeJS 推着绕赤道转上十二圈。(老实说,到现在为止,让其他所有系统都切换字节序所带来的不兼容性、转换开销、数据损坏和编程难题,难道不是早已远远超过了选择大小端字节序中较差者作为标准可能带来的任何想象得到的劣势了吗?)

**糟糕的妥协只会让所有人都不满意。**Python 2 和 Python 3 的并存就是一个典型例子。两个版本都拥有庞大的用户群,可能会无限期地并存下去。然而,这种状态对整个 Python 社区来说都是一个持续的痛点。它不仅没有实现规模效应(两个半社区的总和远不如一个统一的社区),还给所有 Python 使用者带来了额外的复杂性和潜在的错误风险。开发者们不得不确保正确安装多个版本,并时刻谨慎地使用恰当的版本。更为棘手的是,Python 2 因此获得了一种隐性优势,能够在其他系统中造成「比特蔓延」。这些系统不得不为兼容 Python 2 而做出调整,而非反过来。这实际上是 Python 2 用户在被动地享受一种补贴,可以说是在「收取」过去 Python 程序员(讽刺的是,其中许多人正是 Python 3 的支持者)创造的知识产权的「租金」。在这种情况下,Python 2 的用户缺乏动力去学习 Python 3 的新特性。他们可以安于现状,因为他们的程序不会轻易过时。但这种表面上的安逸实际上带来了三重代价:错失 Python 3 带来的新机遇(这点在讨论网络效应时常被忽视)、造成比特蔓延,以及分裂 Python 社区。更不公平的是,这三重代价几乎全部由其他人而非坚持使用 Python 2 的人承担!尽管难以精确计算,但考虑到库/工具维护者与可能数以百万计的下游用户之间的巨大差异,这种成本与收益的比率可能在数量级上存在巨大差距。因此,Python 3 的开发者对这种状况感到不满是完全可以理解的。[13⁠]

战争,以其他形式进行。考虑到这些因素,难怪争论可能变得不那么文明,有些人甚至会诉诸霸凌或向所有人许下空洞承诺:毕竟,这里的利害关系可能相当高,即使不是生死攸关,也会对一个人的生活质量产生重大影响。尽管如此,这种分散且无组织的口水战和霸凌行为是不明智的;即便它有效,或许还能找到正当理由,但历史经验表明,自发的口水战几乎从未奏效,只会造成破坏。人们应该「保持友善,至少在你能协调一致地表现出不友善之前」

解释技术圣战

这解释了为什么某些技术领域会出现圣战而其他领域不会,为什么这些争端会持续存在,为何最相似的群体之间往往会爆发最激烈的冲突(这不是源于「自恋」,而是出于实用主义考虑,因为他们在争夺相同的稀缺资源)[14⁠],为什么这些争论如此缺乏理性且难以化解,为什么参与者感到不得不诉诸霸凌和情感伤害,为什么这些争论确实至关重要,以及它们通常会如何演变。

比特蔓延确实是一个真实存在的现象,但它带来的成本和收益分布并不均衡。这种影响通常程度适中,主要作用于选择有限的特定用户群体。对这些用户而言,不做选择反而比做出任何选择都更为不利。然而,没有任何机制能够强制整个社区做出统一选择,即使在某些情况下,做出一个选择比选择具体哪一个更为重要。

基于这种观点,我们可以探讨一些减轻比特蔓延带来负面影响的方法。

协调迁移

诉诸公共精神;经济激励?;有意地中心化以解决协调问题

这种协调问题并非计算机领域独有。没有企鹅愿意成为第一个跳下冰山的——但鱼儿不会自动送上门来。与其进行临时升级,或是诉诸口水战、宣传攻势和霸凌那些有不同需求或可以理解地只顾自身利益的人,难道没有更好的方法吗?

诉诸公共精神。 一个更好的方法是提高人们对成本的认识。升级或替代方案真的有必要吗?如果确实有必要,那么像 Armin Ronacher 这样的坚持者应该以理性的方式了解他们的行为所造成的成本,而不是对他们怒气冲冲,或在网上用虚假论点霸凌他们。

**经济激励?**既然需要进行升级,我们可以考虑采用经济手段:设立基金来支付维护者进行过渡的费用,或在必要时资助一个分支项目(一旦过渡成为既定事实,且现状开始向相反方向产生渐进式变化时,可以将项目归还给原维护者)。在这种情况下,保证合同可能是一种有效的社会技术手段(为维护者支付的升级成本低于总体收益,因此符合卡尔多改进)。另一种方式是公开承诺在特定日期进行过渡,类似于 Python 2 被宣布终止支持的做法。

有意地中心化以解决协调问题。从比特蔓延的角度来看,那些试图通过竭尽全力改进模块化并尝试同时支持多个生态系统来达成和解的努力,实际上是一种自我毁灭的愚蠢行为:找到一个进行转型的「制高点」可能是最有效的单一策略,而且它会自然而然地执行。(这种策略可能看起来有些不光明正大,但在处理协调问题时,善意反而可能造成伤害。)一个生态系统越是遵循「只此一途」的原则,协调就越容易。以 Go 语言 为例,它可能不是一种卓越的编程语言,但大家都在使用(并强制他人使用)的 gofmt 工具不是很好吗?它以某种方式统一格式化 Go 代码,从而将 Go 程序员从「自由」的束缚中解放出来。

事实上,也许我们应该重新审视「终身仁慈独裁者」或项目维护者的真正角色。他们的职责不应该是日常的项目维护或组织工作,而是作为一个最终权威,引导社区走向一个新的、更好的平衡状态——这种角色越是谨慎使用,就越显得珍贵。